export default class Webgl {
  constructor(canvasElement) {
    this.canvasElement = canvasElement;
    this.gl = getWebGLContext(this.canvasElement);
    this.canvasContext = document.createElement('canvas').getContext('2d');
    this.fillColor = 'rgba(1.0,1.0,1.0,1.0)';
  }

  _colorTransform(sourceColor = 'rgba(0.0, 0.0, 0.0, 1.0)') {
    if (sourceColor.startsWith('#')) {
      if (sourceColor.length === 4) {
        let rgba = [];
        let tempArr = sourceColor.split('');
        for (let i = 1; i < 4; i++) {
          rgba.push(Number('0x' + tempArr[i] + tempArr[i]) / 255);
        }
        rgba.push(1.0);
        return rgba;
      }

      if (sourceColor.length === 7) {
        let rgba = [];
        let tempArr = sourceColor.split('');
        for (let i = 1; i < 7; i += 2) {
          rgba.push(Number('0x' + tempArr[i] + tempArr[i + 1]) / 255);
        }
        rgba.push(1.0);
        return rgba;
      }
    }

    if (sourceColor.startsWith('rgb')) {
      let tempArr = sourceColor.match(/\d[^\,\)]*/mg);
      let rgba = [];
      let offset = 0;
      (sourceColor.length === 4) && (offset = 1);
      for (let i = 0; i < tempArr.length - offset; i++) {
        rgba.push(Number(tempArr[i]) / 255);
      }
      rgba[3] = tempArr[3] || 1.0;
      return rgba;
    }

    return [0.0, 0.0, 0.0, 1.0];
  }

  clearWebglCanvasStatus(clearColor) {
    let clearColorOfWebgl = this._colorTransform(clearColor);
    this.gl.clearColor(...clearColorOfWebgl);
    this.gl.clear(this.gl.COLOR_BUFFER_BIT);
  }

  clearCanvasContext() {
    let width = this.canvasContext.canvas.width;
    let height = this.canvasContext.canvas.height;
    this.canvasContext.clearRect(0, 0, width, height);
  }

  /**
   * 
   * @param {object} webglDataObj 
   * @param {array} webglDataObj.pointData
   * @param {array} webglDataObj.dataIndex
   */
  renderPolygon(webglDataObj) {
    const VSHADER_SOURCE = `
            attribute vec4 a_Position;
            attribute float a_PointSize;
            void main(){
            gl_Position = a_Position;
            }`;

    const FSHADER_SOURCE = `
            precision mediump float;
            uniform vec4 u_FragColor;
            void main(){
                gl_FragColor = u_FragColor;
            }`;

    let gl = this.gl;
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
      console.log('Failed to initialize shaders');
      return;
    }

    let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    let u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Position);

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(webglDataObj.pointData), gl.DYNAMIC_DRAW);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(webglDataObj.dataIndex), gl.DYNAMIC_DRAW);

    gl.uniform4f(u_FragColor, ...this._colorTransform(this.fillColor));
    gl.drawElements(gl.TRIANGLES, webglDataObj.dataIndex.length, gl.UNSIGNED_SHORT, 0);
  }

  /**
   * 
   * @param {string} textContent 
   * @param {array} coordinates 
   */
  renderText(textContent, coordinates, style) {
    const VSHADER_SOURCE =
      `attribute vec4 a_Position;
       attribute vec2 a_TexCoord;
       varying vec2 v_TexCoord;
       void main() {
          gl_Position = a_Position;
          v_TexCoord = a_TexCoord;
      }`;
    const FSHADER_SOURCE =
      `#ifdef GL_ES
       precision mediump float;
       #endif
       uniform sampler2D u_Sampler;
       varying vec2 v_TexCoord;
       void main() {
         gl_FragColor = texture2D(u_Sampler, v_TexCoord);
       }`;

    let gl = this.gl;
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
      console.log('Failed to initialize shaders');
      return;
    }

    const getPowerOfTwoForTextCanvas = value => {
      let pow = 1;
      while (pow < value) {
        pow = pow * 2;
      }
      return pow;
    };

    let fontSize = Number(style.font && style.font.match(/\d+px/mg)[0].replace(/px/mg, '')) || 10;
    let textCanvasWidth = getPowerOfTwoForTextCanvas(parseInt(style.wordWrapWidth, 10) || 200);
    let lineWordNumber = Math.floor(textCanvasWidth / fontSize);
    let textCanvasHeight = getPowerOfTwoForTextCanvas(Math.ceil(textContent.length / lineWordNumber) * fontSize);
    this.canvasContext.canvas.height = textCanvasHeight;
    this.canvasContext.canvas.width = textCanvasWidth;

    let styleObjKeys = Object.keys(style);

    styleObjKeys.forEach(key => {
      if ((key in this.canvasContext) && typeof this.canvasContext[key] !== 'function') {
        this.canvasContext[key] = style[key];
      }
    })

    let lineHeight = fontSize;
    let wordsLength = 0;

    for (let i = 0, length = textContent.length; i < length; i++) {
      let tempWidth = this.canvasContext.measureText(textContent.substring(i, i + 1)).width;
      this.canvasContext.fillText(textContent.substring(i, i + 1), wordsLength, lineHeight);
      wordsLength += tempWidth;
      if (wordsLength >= textCanvasWidth - tempWidth) {
        lineHeight += fontSize;
        wordsLength = 0;
      }
    }

    let textCanvasHalfWidthOnWebGl = this.canvasContext.canvas.width / this.canvasElement.offsetWidth;
    let textCanvasHalfHeightOnWebGl = this.canvasContext.canvas.height / this.canvasElement.offsetHeight;

    let x = coordinates[0];
    let y = coordinates[1];
    let [x0, y0, x1, y1, x2, y2, x3, y3] = [x - textCanvasHalfWidthOnWebGl, y + textCanvasHalfHeightOnWebGl, x - textCanvasHalfWidthOnWebGl, y - textCanvasHalfHeightOnWebGl, x + textCanvasHalfWidthOnWebGl, y + textCanvasHalfHeightOnWebGl, x + textCanvasHalfWidthOnWebGl, y - textCanvasHalfHeightOnWebGl];

    const rotatePolygon = (fourPoints, angle) => {

      const getRotatePoint = (p, centerPoint, angle) => {
        return [(p[0] - centerPoint[0]) * Math.cos(angle) - (p[1] - centerPoint[1]) * Math.sin(angle) + centerPoint[0],
        (p[0] - centerPoint[0]) * Math.sin(angle) + (p[1] - centerPoint[1]) * Math.cos(angle) + centerPoint[1]]
      }

      let centerCoordinates = [(x0 + x1 + x2 + x3) / 4, (y0 + y1 + y2 + y3) / 4];
      let data = [];
      for (let i = 0; i < fourPoints.length; i += 2) {
        data.push(...getRotatePoint([fourPoints[i], fourPoints[i + 1]], centerCoordinates, angle))
      }
      return data;
    }

    style.rotate && ([x0, y0, x1, y1, x2, y2, x3, y3] = [...rotatePolygon([x0, y0, x1, y1, x2, y2, x3, y3], style.rotate || 0)]);

    let verticesTexCoords = new Float32Array([
      x0, y0, 0, 1,
      x1, y1, 0, 0,
      x2, y2, 1, 1,
      x3, y3, 1, 0
    ]);

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.DYNAMIC_DRAW);

    const FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

    let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    let u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
    let a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');

    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
    gl.enableVertexAttribArray(a_TexCoord);

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());

    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvasContext.canvas);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.uniform1i(u_Sampler, 0);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  }
}